`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/10/21 17:10:50
// Design Name: 
// Module Name: MDIO_Drive
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module mdio_drive(
    input           i_clk               ,
    input           i_rst               ,

    output          o_mdc               ,
    inout           io_mdio             ,

    input  [1 :0]   i_user_op           ,//01-W   10-R
    input  [4 :0]   i_user_phy_addr     ,
    input  [4 :0]   i_user_reg_addr     ,
    input  [15:0]   i_user_reg_data     ,
    input           i_user_valid        ,
    output          o_user_ready        ,
    output [15:0]   o_user_read_data    ,
    output          o_user_read_valid   
);

reg  [1 :0]         r_user_op           ;
reg  [63:0]         r_mdio_data         ;
reg                 ro_user_ready       ;
reg  [15:0]         ro_user_read_data   ;
reg                 ro_user_read_valid  ;
reg                 ro_mdio             ;
reg                 r_mdio_ctrl         ;
reg  [15:0]         r_cnt               ;
reg                 r_run               ;

wire                w_mdio              ;
wire                w_active            ;

assign o_mdc             = ~i_clk       ;
assign io_mdio           = r_mdio_ctrl ? ro_mdio    : 'bz   ;
assign w_mdio            = io_mdio              ;
assign o_user_ready      = ro_user_ready        ;
assign o_user_read_data  = ro_user_read_data    ;
assign o_user_read_valid = ro_user_read_valid   ;
assign w_active          = i_user_valid & o_user_ready  ;


always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_user_op <= 'd0;
    else if(w_active)
        r_user_op <= i_user_op;
    else 
        r_user_op <= r_user_op;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_mdio_data <= 'd0;
    else if(r_run)
        r_mdio_data <= r_mdio_data << 1;
    else if(w_active & i_user_op == 2'b10)
        r_mdio_data <= {32'HFFFFFFFF,2'b01,2'b10,i_user_phy_addr,i_user_reg_addr,2'bzz,16'd0};
    else if(w_active & i_user_op == 2'b01)
        r_mdio_data <= {32'HFFFFFFFF,2'b01,2'b01,i_user_phy_addr,i_user_reg_addr,2'b10,i_user_reg_data};
    else 
        r_mdio_data <= r_mdio_data;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_ready <= 'd1;
    else if(r_run && r_cnt == 64 - 1)
        ro_user_ready <= 'd1;
    else if(w_active)
        ro_user_ready <= 'd0;
    else 
        ro_user_ready <= ro_user_ready;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_run <= 'd0;
    else if(r_run && r_cnt == 64 - 1)
        r_run <= 'd0;
    else if(w_active)
        r_run <= 'd1;
    else 
        r_run <= r_run;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_cnt <= 'd0;
    else if(r_run && r_cnt == 64 - 1)
        r_cnt <= 'd0;
    else if(r_run)
        r_cnt <= r_cnt + 1;
    else 
        r_cnt <= r_cnt;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_mdio_ctrl <= 'd0;
    else if(r_run && r_user_op == 2'b01)
        r_mdio_ctrl <= 'd1;
    else if(r_run && r_user_op == 2'b10 && r_cnt < 46)
        r_mdio_ctrl <= 'd1;
    else 
        r_mdio_ctrl <= 'd0;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_mdio <= 'd0;
    else if(r_run)
        ro_mdio <= r_mdio_data[63];
    else 
        ro_mdio <= 'd0;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_read_data <= 'd0;
    else if(r_run && r_user_op == 2'b10 && r_cnt >= 48)
        ro_user_read_data <= {ro_user_read_data[14:0],w_mdio};
    else 
        ro_user_read_data <= ro_user_read_data;
end
 

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_read_valid <= 'd0;
    else if(r_run && r_user_op == 2'b10 && r_cnt == 63)
        ro_user_read_valid <= 'd1;
    else 
        ro_user_read_valid <= 'd0;
end
endmodule
